#include "sys.h"
#include "log.h"
#include "thread_pool.h"
#include "dial.h"
#include "cJSON.h"
#include "udpspeed_tactics.h"
#include "data_report.h"
#include "trans.h"
#include "devinfo.h"
#include "comm_param.h"
#include "miss.h"
#include "miss_interface.h"
#include "miss_error.h"

static float *Rate_Arr = NULL;
static int Rate_Arr_idx = 0;
static int Total_Bytes = 0;


struct down_up_urls {
    int up_count, down_count;
    char** up;
    char** down;
};


static void _free_down_up_urls(struct down_up_urls *urls){
    int i =0;

    for(i =0; i < urls->up_count; i++){
        free(urls->up[i]);
    }
    for(i =0; i < urls->down_count; i++){
        free(urls->down[i]);
    }
    free(urls->down);
    free(urls->up);
}
static int _get_node_url(char* url, char* probeId, char* type, struct down_up_urls* urls)
{
    cJSON *root = NULL, *resp = NULL, *arr, *obj, *data;
    char* post_data;
    int ret = 1, i = 0;

    root = cJSON_CreateObject();
    cJSON_AddStringToObject(root, "speedType", type);
    cJSON_AddStringToObject(root, "probeId", probeId);

    post_data = cJSON_Print(root);
    if ((resp = http_post(url, post_data)) == NULL) {
        log_error("Get node url error");
        ret = 0;
        goto out;
    }

    obj = cJSON_GetObjectItem(resp, "status");
    if (strcmp(obj->valuestring, "1") != 0) {
        log_error("Get node url failed");
        ret = 0;
        goto out;
    }
    arr = cJSON_GetObjectItem(resp, "uploadUrlList");
    urls->up_count = cJSON_GetArraySize(arr);
    urls->up = calloc(urls->up_count, sizeof(char*));
    for (i = 0; i < urls->up_count; i++) {
        obj = cJSON_GetArrayItem(arr, i);
        data = cJSON_GetObjectItem(obj, "url");
        urls->up[i] = strdup(data->valuestring);
    }

    arr = cJSON_GetObjectItem(resp, "downloadUrlList");
    urls->down_count = cJSON_GetArraySize(arr);
    urls->down = calloc(urls->down_count, sizeof(char*));
    for (i = 0; i < urls->down_count; i++) {
        obj = cJSON_GetArrayItem(arr, i);
        data = cJSON_GetObjectItem(obj, "url");
        urls->down[i] = strdup(data->valuestring);
    }

out:
    cJSON_Delete(root);
    if (resp)
        cJSON_Delete(resp);
    free(post_data);
    return ret;
}
static int accept_process_data(PProcessResult pData, IN void* arg)
{
    // log_trace("udpspeed: rate = %.2f Mbps, sendRate = %.2f, lost = %.2f", pData->dRate, pData->dSendRate, pData->dLost);
    if(pData->dRate < 0){
        return 0;
    }
    Rate_Arr[Rate_Arr_idx++] = pData->dRate * 1000 * 1000;
    Total_Bytes += (pData->dRate * 1000 * 1000 / 8);
    return 0;
}
static int rate_compare(void *a, void *b){
    float *aa = (float *)a;
    float *bb = (float *)b;

    if(*aa > *bb)return 1;
    if(*aa == *bb) return 0;
    if(*aa < *bb) return -1;
}
static float cal_avg(int s_idx, int e_idx, float *arr){
    int i =0;
    float sum = 0;

    log_trace("s_idx:%d, e_idx:%d", s_idx, e_idx);

    if(e_idx == 0)
        return 0;

    for (i = s_idx; i < e_idx; ++i)
    {
        sum += arr[i];
    }

    return sum / (e_idx - s_idx);
}
static void _show_rate_detail(float *rate_arr, int count){
    int i = 0;

    for ( i = 0; i < count; ++i)
    {
        log_trace("rate:%.2f, idx:%d", rate_arr[i], i);
    }
}
int do_udpspeed_test(cJSON *obj, char *ipstr, int tcpPort, int udpPort, int timeout, float discardPercent){
    int ret = -1, tmp_idx = 0, down_success = 0;
    char tmp_str[32] = { 0 };
    float avg_speed = 0;

    Rate_Arr_idx = 0;
    Total_Bytes = 0;
    Rate_Arr = calloc(timeout / 1000, sizeof(float));
    // log_trace("ip:%s, tcpPort:%d, udpPort:%d, bdcode_val:%d, timeout:%d", urls.down[0], downTactics->tcpPort, downTactics->udpPort, commReqParam.bdcode_val, downTactics->timeout);
    ret = miss_test_start(ipstr, tcpPort, udpPort, ((int)Mbps(commReqParam.bdcode_val)), timeout / 1000, 1, 1000, 2, accept_process_data);
    if(ret < 0){
        log_error("miss_test_start ret:%d", ret);
    }else{
        qsort(Rate_Arr, Rate_Arr_idx, sizeof(float), rate_compare);
        if(Debug_mode()){
            _show_rate_detail(Rate_Arr, Rate_Arr_idx);
        }
        tmp_idx = Rate_Arr_idx * discardPercent;
        avg_speed = cal_avg(tmp_idx, Rate_Arr_idx, Rate_Arr);
        sprintf(tmp_str, "%.0f", KBps(Rate_Arr[tmp_idx] / 8));
        cJSON_AddStringToObject(obj, "downMinSpeed", tmp_str);

        if(Rate_Arr_idx)
            sprintf(tmp_str, "%.0f", KBps(Rate_Arr[Rate_Arr_idx - 1] / 8));
        else
            sprintf(tmp_str, "%.0f", KBps(Rate_Arr[0] / 8));
        cJSON_AddStringToObject(obj, "downMaxSpeed", tmp_str);

        sprintf(tmp_str, "%.0f", KBps(avg_speed / 8));
        cJSON_AddStringToObject(obj, "downAvgSpeed", tmp_str);
        cJSON_AddNumberToObject(obj, "downActualDuration", timeout);
        cJSON_AddNumberToObject(obj, "downActualSize", Total_Bytes);
        log_info("Udp Download success");

        if(avg_speed == 0 ){
            down_success = 0;
        }else{
            down_success = 1;
        }
    }

    free(Rate_Arr);
    miss_test_stop();
    return down_success;
}
void* do_udpspeed_tactics(void* arg, char* nodeUrl, char* probeId)
{

    udpspeed_tactics_t* udpspeed = (udpspeed_tactics_t*)arg;
    struct udpspeedTactics* upTactics = TAILQ_FIRST(&udpspeed->up_head);
    struct udpspeedTactics* downTactics = TAILQ_FIRST(&udpspeed->down_head);
    cJSON *result = NULL, *obj = NULL, *arr = NULL;
    struct down_up_urls urls;

    char tmp_str[32] = { 0 };
    char topic[32] = { 0 };
    char *post_data = NULL, reportTime[32], collectTime[32];
    uint32_t cur_netrate = 0;
    int tmp_idx = 0;
    int ret = 0;
    int down_success = 0, up_success = 0;
    pthread_t thid = 0;


    if(udpspeed->down_count == 0 && udpspeed->up_count == 0){
        return NULL;
    }

    memset(&urls, 0, sizeof(urls));
    if (!_get_node_url(nodeUrl, probeId, "udp", &urls)) {
        return NULL;
    }

    result = cJSON_CreateObject();
    if(!result){
        log_trace("cJSON_CreateObject failed.");
        return NULL;
    }
    cJSON_AddStringToObject(result, "dialTaskId", udpspeed->taskId);
    cJSON_AddStringToObject(result, "version", udpspeed->version);
    cJSON_AddStringToObject(result, "indexType", "udp");
    get_timestring(reportTime);
    cJSON_AddStringToObject(result, "reportTime", reportTime);
    get_timestring(collectTime);
    cJSON_AddStringToObject(result, "collectTime", collectTime);
    // pthread_mutex_lock(&httpspeed->mutex);
    arr = cJSON_CreateArray();
    if(!arr){
        log_trace("cJSON_CreateArray failed.");
        cJSON_Delete(result);
        return NULL;
    }
    obj = cJSON_CreateObject();
    if(!obj){
        log_trace("cJSON_CreateObject failed.");
        cJSON_Delete(result);
        cJSON_Delete(arr);
        return NULL;
    }
        
    SPEEDTEST_LOCK();

    //由于光猫建立了两条通道，对下载速度互不影响，不用检测背景流量,
    //但光猫在路由模式下则需检测背景流量。
    log_trace("check if need monitor backgroud flow.");
    if(devinfo_is_bridge() == 0){
        cur_netrate = devinfo_monitor_net_rate(&devInfo);
        log_info("cur_netrate:%.2f KBps, threshold:%.2f KBps", KBps(cur_netrate), KBps(downTactics->threshold * commReqParam.bdcode_val));
    }

    if(cur_netrate > (downTactics->threshold * commReqParam.bdcode_val) && commReqParam.bdcode_val > 0 && downTactics->threshold > 0){
        
        sprintf(tmp_str, "%.0f", KBps(cur_netrate));
        cJSON_AddStringToObject(obj, "bgFlowRate", tmp_str);
        cJSON_AddNumberToObject(obj, "resultCode", -1);

        cJSON_AddStringToObject(obj, "downMinSpeed", "0");
        cJSON_AddStringToObject(obj, "downMaxSpeed", "0");
        cJSON_AddStringToObject(obj, "downAvgSpeed", "0");
        cJSON_AddNumberToObject(obj, "downActualDuration", 0);
        cJSON_AddNumberToObject(obj, "downActualSize", 0);
        
        cJSON_AddStringToObject(obj, "upMinSpeed", "0");
        cJSON_AddStringToObject(obj, "upMaxSpeed", "0");
        cJSON_AddStringToObject(obj, "upAvgSpeed", "0");
        cJSON_AddNumberToObject(obj, "upActualDuration", 0);
        cJSON_AddNumberToObject(obj, "upActualSize", 0);
        
        
    }else{

        cJSON_AddNumberToObject(obj, "bgFlowRate", 0);
        cJSON_AddNumberToObject(obj, "resultCode", 0);

        if (downTactics) {
            log_trace("do udpSpeed download");
            strcpy(topic, downTactics->kafkaTopic);

            down_success = do_udpspeed_test(obj, urls.down[0], 
                downTactics->tcpPort, downTactics->udpPort, 
                downTactics->timeout, downTactics->discardPercent);
        }
        if (upTactics) {
            log_trace("do udpSpeed upload");
            //TODO
        }
        _free_down_up_urls(&urls);
    }
    SPEEDTEST_UNLOCK();
    
    get_timestring(collectTime);
    cJSON_AddStringToObject(obj, "collectTime", collectTime);
    if(upTactics){
        cJSON_AddStringToObject(obj, "businessTag", upTactics->businessTag);
    }else if(downTactics){
        cJSON_AddStringToObject(obj, "businessTag", downTactics->businessTag);
    }
    
    if(down_success){
        cJSON_AddItemToArray(arr, obj);
    }else{
        cJSON_Delete(obj);
    }
    
    cJSON_AddItemToObject(result, "udpSpeedList", arr);
    post_data = cJSON_Print(result);
    if(Debug_mode()){
        log_debug("udpSPEED post_data(%p): %s", post_data, post_data);
    }
    // data_report(httpspeed->kafka_url, topic, post_data, strlen(post_data) );
    free(post_data);
    // cJSON_Delete(result);
    // pthread_cond_signal(&httpspeed->cond);
    // pthread_mutex_unlock(&httpspeed->mutex);
    return (void *)result;
}